package com.laivi.basic.util;

import java.io.File;
import java.io.FileFilter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;

import com.laivi.basic.annotation.orm.Table;
import com.laivi.basic.common.constants.Constant;
import com.laivi.basic.model.orm.ClassFilter;

/**
 * @author laivi.zhu@gmail.com
 * @date 2014-6-10
 * description:
 */
public class LangUtil {
	
	/**
     * 使用当前线程的ClassLoader加载给定的类
     * 
     * @param className
     *            类的全称
     * @return 给定的类
     * @throws ClassNotFoundException
     *             如果无法用当前线程的ClassLoader加载
     */
    public static Class<?> loadClass(String className) throws ClassNotFoundException {
        try {
            return Thread.currentThread().getContextClassLoader().loadClass(className);
        }
        catch (ClassNotFoundException e) {
            return Class.forName(className);
        }
    }
    
    /**
     * 判断className 是否存在
     * @date 2014-6-11
     * @param className
     * @return
     */
    public static boolean isExist(String className){
    	try{
    		Class.forName(className);
    		return true;
    	}catch(ClassNotFoundException e){
    		return false;
    	}
    }
    
    public static Field[] getFields(Class<?> klass,boolean noStatic, boolean noMember, boolean noFinal, boolean noInner){
		Class<?> cc=klass;
		Map<String, Field> map = new LinkedHashMap<String, Field>();
        while (null != cc && cc != Object.class) {
            Field[] fs = cc.getDeclaredFields();
            for (int i = 0; i < fs.length; i++) {
                Field f = fs[i];
                int m = f.getModifiers();
                if (noStatic && Modifier.isStatic(m))
                    continue;
                if (noFinal && Modifier.isFinal(m))
                    continue;
                if (noInner && f.getName().startsWith("this$"))
                    continue;
                if (noMember && !Modifier.isStatic(m))
                    continue;
                if (map.containsKey(fs[i].getName()))
                    continue;

                map.put(fs[i].getName(), fs[i]);
            }
            cc = cc.getSuperclass();
        }
        return map.values().toArray(new Field[map.size()]);
	}
	
	public static Field[] getFields(Class<?> klass){
		return LangUtil.getFields(klass, true, false, true, true);
	}
	
	public static <AT extends Annotation> Field[] getFields(Class<?> klass,Class<AT> annotation){
		 List<Field> fields = new LinkedList<Field>();
	        for (Field f : LangUtil.getFields(klass)) {
	            if (f.isAnnotationPresent(annotation))
	                fields.add(f);
	        }
	        return fields.toArray(new Field[fields.size()]);
	}
	
	public static Method getMethod(Class<?> klass,String fieldName){
		String fn = DataUtil.capitalize(fieldName);
	    String _get = "get" + fn;
	    String _is = "is" + fn;
		for (Method method : klass.getMethods()) {
		    if (method.getParameterTypes().length != 0)
		        continue;
		    if (!method.isAccessible()) //有些时候,即使是public的方法,也不一定能访问
		    	method.setAccessible(true);
		    if (_get.equals(method.getName()))
		        return method;
		    if (_is.equals(method.getName())) {
		        return method;
		    }
		    if (fieldName.equals(method.getName()))
		        return method;
		}
		return null;
	}
	
	public static String getClassType(Class<?> klass){
		return klass.getSimpleName();
	}
	
	public static List<Class<?>> scanPackage(String packageName){
		return scanPackage(packageName,null);
	}
	
	public static List<Class<?>> scanTable(String packageName){
		return scanPackage(packageName,new ClassFilter(){

			@Override
			public boolean accept(Class<?> klass) {
				Table table=klass.getAnnotation(Table.class);
				if(table!=null){
					return true;
				}
				return false;
			}});
	}
	
	public static List<Class<?>> scanPackage(String packageName,ClassFilter classFilter){
		if(!DataUtil.isEmpty(packageName)){
			List<Class<?>> classes = new ArrayList<Class<?>>();
			String packageValue=packageName.lastIndexOf(".") != packageName.length() - 1 ? packageName + "." : packageName;
			for(String classPath:System.getProperty("java.class.path").split(System.getProperty("path.separator"))){
				fillClasses(new File(classPath),packageValue,classFilter,classes);
			}
			return classes;
		}
		return null;
	}
	
	public static void fillClasses(File classFile,String packageName,ClassFilter classFilter,List<Class<?>> classes){
		if(classFile.isDirectory()){
			for (File file : classFile.listFiles(new FileFilter(){
				@Override
				public boolean accept(File pathname) {
					return pathname.getName().endsWith(".class") || pathname.isDirectory() || pathname.getName().endsWith(".jar");
				}
			})) {
				fillClasses(file, packageName,classFilter,classes);
			}
		}else if(classFile.getName().endsWith(".class")){
			final String filePathWithDot = classFile.getAbsolutePath().replace(File.separator, ".");
			int subIndex = -1;
			if ((subIndex = filePathWithDot.indexOf(packageName)) != -1) {
				final String className = filePathWithDot.substring(subIndex).replace(".class",Constant.EMPTY);
				if(className.indexOf(packageName) == 0){
					try {
						final Class<?> clazz = Class.forName(className, Boolean.FALSE,LangUtil.class.getClassLoader());
						if(classFilter==null ||classFilter.accept(clazz)){
							classes.add(clazz);
						}
					} catch (Throwable ex) {
					}
				}
			}
		}else if(classFile.getName().endsWith(".jar")){
			try {
				for (ZipEntry entry : Collections.list(new ZipFile(classFile).entries())) {
					if(entry.getName().endsWith(".class")){
						final String className = entry.getName().replace("/", ".").replace(".class", Constant.EMPTY);
						if(className.indexOf(packageName) == 0){
							final Class<?> clazz = Class.forName(className, Boolean.FALSE,LangUtil.class.getClassLoader());
							if(classFilter==null ||classFilter.accept(clazz)){
								classes.add(clazz);
							}
						}
					}
				}
			} catch (Throwable ex) {
			}
		}
	}
}
